/*
 * Departamento de Ciência da Computação
 * Tradutores 02/2009 - turma A
 * Professor Marco Aurélio
 * Alunos: Danilo Gaby Andersen Trindade - 06/82039
 *         Alex Rodrigo de Oliveira      - 06/30403
 */

#include <cstdio>
#include <list>
#include "tipos.h"
#include "op.h"

using namespace std;

static frame globalframe, scopeframe;

static box *actual;

static list<int16_t> used, freed;

static void instr(int cod, int rs, int a1, int a2);

static void settype(int16_t rs, char tp);

static void freereg();

static int16_t getreg();

static void writecode();


/*
 * TODO: nada?
 */
void cod_init() {
}


/*
 * Chama a função de escrever o bytecode e libera tudo.
 */
void cod_end() {
    writecode();
    globalframe.clear();
    scopeframe.clear();
    used.clear();
    freed.clear();
}


/*
 * Cria global fixo.
 */
int cod_reg0() {
    reg newreg = {FREE, 0};
    globalframe.push_back(newreg);
    return -(globalframe.size() - 1);
}


/*
 * Cria local fixo.
 */
int cod_reg1() {
    reg newreg = {FREE, 0};
    scopeframe.push_back(newreg);
    return scopeframe.size() - 1;
}


/*
 * Cria global fixo e cria um box.
 */
int cod_fn() {
    actual = new box();
    scopeframe.clear();
    reg newreg = {FUNCT, (int64_t)actual};
    globalframe.push_back(newreg);
    return -(globalframe.size() - 1);
}


/*
 * Finaliza frame e box (terminou a função).
 */
int cod_fn_end() {
    actual->framesize = scopeframe.size();
    return 0;
}


/*
 * MOVE RS A1 : atribuição.
 */
int cod_move(int16_t rs, int16_t a1, char tp) {
    settype(rs, tp);
    instr(MOV, rs, a1, 0);
    freereg();
    return rs;
}


/*
 * OP X A1 A2
 */
int cod_eq(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(EQ, rt, a1, a2);
    return rt;
}

int cod_lt(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(LT, rt, a1, a2);
    return rt;
}

int cod_gt(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(GT, rt, a1, a2);
    return rt;
}

int cod_le(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(LE, rt, a1, a2);
    return rt;
}

int cod_ge(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(GE, rt, a1, a2);
    return rt;
}

int cod_ne(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(NE, rt, a1, a2);
    return rt;
}

int cod_add(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(ADD, rt, a1, a2);
    return rt;
}

int cod_sub(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(SUB, rt, a1, a2);
    return rt;
}

int cod_mult(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(MULT, rt, a1, a2);
    return rt;
}

int cod_mod(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(MOD, rt, a1, a2);
    return rt;
}

int cod_div(int16_t a1, int16_t a2) {
    int16_t rt = getreg();
    instr(DIV, rt, a1, a2);
    return rt;
}


/*
 * IMM X val
 */
int cod_imm(int32_t val) {
    int16_t rt = getreg();
    
    int16_t hi = val >> 16;
    int16_t lo = val;
    instr(IM, rt, hi, lo);
    return rt;
}


/*
 * Acrescenta uma operação no box atual.
 */
static void instr(int cod, int rs, int a1, int a2) {
    op instr = {cod, rs, a1, a2};
    actual->push_back(instr);
}


static void settype(int16_t rs, char tp) {
    if (rs < 0) globalframe[-rs].tp = tp;
    else scopeframe[rs].tp = tp;
}


/*
 * Libera (marca como livre) todos os registradores temporários.
 */
static void freereg() {
    freed.splice(freed.end(), used);
}


/*
 * Devolve um registrador temporário que não está sendo usado.
 * Se for preciso, cria um.
 */
static int16_t getreg() {
    int16_t pos;
    
    if (freed.empty()) {
        reg newreg = {FREE, 0};
        scopeframe.push_back(newreg);
        pos = scopeframe.size() - 1;
        used.push_back(pos);
    }

    else {
        pos = freed.back();
        freed.pop_back();
        used.push_back(pos);
    }
    
    return pos;
}


/*
 * Escreve o bytecode final.
 */
static void writecode() {
    for (frame::iterator it = globalframe.begin(); it != globalframe.end(); ++it) {
        printf("%d\n", it->tp);
        box *fn = (box*)it->val;
        switch (it->tp) {
        case FUNCT:
            printf("%d\n", fn->size());
            for (box::iterator jt = fn->begin(); jt != fn->end(); ++jt) {
                printf("%d\t%d\t%d\t%d\n", jt->cod, jt->rs, jt->a1, jt->a2);
            }
            break;
        case INT:
            printf("%lld\n", it->val);
            break;
        case LIST:
            printf("---\n");
            break;
        default:
            fprintf(stderr, "Tipo de dado não suportado: %d\n", it->tp);
        }
        putchar('\n');
    }
    
}
